package com.sc.byahi.user.client;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sc.byahi.user.client.components.HistoryTokens;
import com.sc.byahi.user.shared.InitDataVO;
import com.sc.byahi.user.shared.Profile;
import com.sc.byahi.user.shared.ProfileMetadata;
import com.sc.byahi.user.shared.ProfileOwner;
import com.sc.byahi.user.shared.SearchBean;
import com.sc.byahi.user.shared.SharedUtil;
import com.sc.byahi.user.shared.constants.Config;
import com.sc.byahi.user.shared.constants.Lists;

public class AppController implements ValueChangeHandler<String> {

	private SearchResultViews resultViews;
	private ProfileSearchView searchView;
	private ProfileIDSearchView idSearchView;
	private Summary summary;
	private SummaryListView summaryListView;
	private Success success;
	private Contact contact;
	private FlexTable summaryView;
	private AdminView adminView;
	private ProfileUpdateView profileUpdateView;
	private Menu menu;
	private Stage stage;
	private UserType userType;

	/**
	 * Assumption: this is unique in one browser session.
	 * TODO: modify data after user adds a profile.
	 */
	private InitDataVO initData = null;
	
	/**
	 * An agent can see profiles on behalf of one user at a time
	 */
	private Long currentProfileId;
	private String currentProfileName = "";
	
	/**
	 * Interest data arranged in type groups
	 */
	private Map<Long, Map<String, Set<Long>>> interestDataComputed = new HashMap<Long, Map<String, Set<Long>>>();
	/**
	 * Map of taregtProfileId - rating
	 */
	private Map<Long, Long> selectedProfileRatings = new HashMap<Long, Long>();

	private static AppController controller = new AppController();

	private AppController() {
		History.addValueChangeHandler(this);
	}

	public String getBrowserCachedData() {
		StringBuffer sb = new StringBuffer();
		sb.append(initData.getInterestData().toString()).append(selectedProfileRatings.toString());
		return sb.toString();
	}

	protected void refreshInitData(final boolean fireEvent) {
		GreetingService.Util.getInstance().getInitData(new AsyncCallback<InitDataVO>() {
			@Override
			public void onSuccess(InitDataVO result) {
				initData = result;
				// A dummy call so that refresh ratings is fired
				Long currentProfileId = null;
				if (!getOwner().getApprovedProfiles().isEmpty()) {
					currentProfileId = getOwner().getApprovedProfiles().get(0);
				} else if (!getOwner().getUnapporvedProfiles().isEmpty()) {
					currentProfileId = getOwner().getUnapporvedProfiles().get(0);
				}
				setCurrentProfileId(currentProfileId);
				
				computeInterestDataCount();
				fireDashboardChange(fireEvent);
//				refreshInterestData(fireEvent);// Async
//				setCurrProfileDataInBrowser(result.getCurrentProfileId(), result.getCurrentProfileName());
			}

			@Override
			public void onFailure(Throwable caught) {
			}
		});
	}
	
	/**
	 * Only one time initialization of lists.
	 */
	protected void refreshLists() {
		GreetingService.Util.getInstance().getLists(new AsyncCallback<Map<Lists, Map<String, List<String>>>>() {

			@Override
			public void onSuccess(Map<Lists, Map<String, List<String>>> result) {
				UIUtil.lists.clear();
				UIUtil.lists.putAll(result);
			}

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub

			}
		});
	}

	public String getCurrentProfileName() {
		return currentProfileName;
	}

	/**
	 * There are 3 places from where this method can be called. First time, when user selects an id and tries to set it as curr profile, or when 
	 * user clicks a summary number and goes to the page.
	 * @param profileId
	 */
	public void setCurrentProfileId(final Long profileId) {
		currentProfileId = profileId;
		setLogoutString();
		// View might have bene rendered...so update it.
		SummaryListView summaryListView = (SummaryListView) getView(View.SUMMARY_LIST);
		summaryListView.setCurrProfileText();
		
		GreetingService.Util.getInstance().setCurrentProfileId(profileId, new AsyncCallback<Map<Long, Long>>() {
			@Override
			public void onSuccess(Map<Long, Long> result) {
				selectedProfileRatings.clear();
				selectedProfileRatings.putAll(result);
			}

			@Override
			public void onFailure(Throwable caught) {
			}
		});
	}

	/**
	 * As rating can be changed by fetching all ratings from database, or it can change when user rates a profile. All the time computation must be
	 * done only when user searches the profile.
	 * 
	 * @return
	 */
	private Map<Long, Set<Long>> computeProfileRatings() {
		Map<Long, Set<Long>> computedProfileRatings = new HashMap<Long, Set<Long>>();
		for (Long i = Config.MIN_RATING; i <= Config.MAX_RATING; i++) {
			computedProfileRatings.put(i, new HashSet<Long>());
		}
		for (Entry<Long, Long> entry : selectedProfileRatings.entrySet()) {
			computedProfileRatings.get(entry.getValue()).add(entry.getKey());
		}
		return computedProfileRatings;
	}

	public void refreshInterestData(final boolean fireEvent) {
		GreetingService.Util.getInstance().findMyInterestData(new AsyncCallback<Map<Long, Map<Long, String>>>() {

			@Override
			public void onSuccess(Map<Long, Map<Long, String>> result) {
				initData.getInterestData().clear();
				initData.getInterestData().putAll(result);
				computeInterestDataCount();
				fireDashboardChange(fireEvent);
			}

			@Override
			public void onFailure(Throwable caught) {
			}
		});
	}

	private void fireDashboardChange(final boolean fireEvent) {
		// refresh menu before history state is fired, menu is not initialized yet
		refreshMenu();
		if (fireEvent) {// one time, only at the beginning
			// means it is used for the first time, so load these files which are loaded one time only
			// Now fire history change which will cause dashboard to open
			String existingToken = History.getToken();
			if ("".equals(existingToken)) {
				History.newItem(HistoryTokens.DASHBOARD, false);
			}
			History.fireCurrentHistoryState();
		}
	}
	public Map<Long, String> getInterestDataForCurrProfileId() {
		if (!initData.getInterestData().containsKey(currentProfileId)) {
			initData.getInterestData().put(currentProfileId, new HashMap<Long, String>());
		}
		return initData.getInterestData().get(currentProfileId);
	}

	
	public void updateInterestDataForCurrProfileId(Long profileId, String action) {
		getInterestDataForCurrProfileId().put(profileId, action);
		computeInterestDataCount();
	}

	/**
	 * Creates summary list from the raw metadata.
	 * 
	 * @return
	 */
	private void computeInterestDataCount() {
		Map<Long, Map<Long, String>> interestMap = initData.getInterestData();
		interestDataComputed.clear();
		for (Long id : interestMap.keySet()) {
			Map<String, Set<Long>> countCatMap = new HashMap<String, Set<Long>>();
			countCatMap.put(ProfileMetadata.IS, new HashSet<Long>());
			countCatMap.put(ProfileMetadata.IA, new HashSet<Long>());
			countCatMap.put(ProfileMetadata.IH, new HashSet<Long>());
			countCatMap.put(ProfileMetadata.RR, new HashSet<Long>());
			countCatMap.put(ProfileMetadata.RA, new HashSet<Long>());
			countCatMap.put(ProfileMetadata.RH, new HashSet<Long>());
			interestDataComputed.put(id, countCatMap);
		}
		for (Entry<Long, Map<Long, String>> entry : interestMap.entrySet()) {
			Long myProfileId = entry.getKey();
			Map<String, Set<Long>> countCatMap = interestDataComputed.get(myProfileId);
			Map<Long, String> targetIdAndType = entry.getValue();
			for (Entry<Long, String> ent : targetIdAndType.entrySet()) {
				countCatMap.get(ent.getValue()).add(ent.getKey());
			}
		}
	}

	private enum View {
		HOME, SEARCH_RESULT, SEARCH, IDSEARCH, DASHBOARD, EDIT, ADMIN, MENU, STAGE, USER_TYPE, SUMMARY, SUMMARY_LIST, SUCCESS, CONTACT
	};

	/**
	 * On demand creation of objects instead of initializing all at once.
	 * 
	 * @param view
	 * @return
	 */
	private Widget getView(View view) {
		switch (view) {
		case ADMIN:
			if (adminView == null) {
				adminView = new AdminView();
			}
			return adminView;
		case SEARCH_RESULT:
			if (resultViews == null) {
				resultViews = new SearchResultViews();
			}
			return resultViews;
		case SEARCH:
			if (searchView == null) {
				searchView = new ProfileSearchView();
			}
			return searchView;
		case IDSEARCH:
			if (idSearchView == null) {
				idSearchView = new ProfileIDSearchView();
			}
			return idSearchView;
		case EDIT:
			if (profileUpdateView == null) {
				profileUpdateView = new ProfileUpdateView();
			}
			// Maybe being called twice
//			// update default list values
//			profileUpdateView.setValues();
			return profileUpdateView;
		case MENU:
			if (menu == null) {
				menu = new Menu();
			}
			refreshMenu();
			return menu;
		case STAGE:
			if (stage == null) {
				stage = new Stage();
			}
			stage.setText();
			return stage;
		case USER_TYPE:
			if (userType == null) {
				userType = new UserType();
			}
			return userType;
		case SUMMARY:
			if (summary == null) {
				summary = new Summary();
			}
			return summary;
		case SUMMARY_LIST:
			if (summaryListView == null) {
				summaryListView = new SummaryListView();
			}
			return summaryListView;
		case SUCCESS:
			if (success == null) {
				success = new Success();
			}
			return success;
		case CONTACT:
			if (contact == null) {
				contact = new Contact();
			}
			return contact;
		case HOME:
		case DASHBOARD:
		default:
			if (summaryView == null) {
				summaryView = new FlexTable();
			}
			// refreshMetadata();
			summaryView.removeAllRows();
			return summaryView;
		}
	}

	public void refreshMenu() {
		// First time menu is null as getView is not called until history is fired
		if (menu == null) {
			menu = new Menu();
		}

		if (initData.getCurrentUserAdmin()) {
			menu.admin.setVisible(true);
		} else {
			menu.admin.setVisible(false);
		}

		menu.search.setVisible(true);
		menu.searchid.setVisible(true);
		if (getOwner().getIsAgency() == null) {
			menu.addprofile.setVisible(false);
			menu.myprofiles.setVisible(false);
		} else {
			// menu.search.setVisible(true);
			// menu.searchid.setVisible(true);
			if (getOwner().getIsAgency()) {
				menu.addprofile.setVisible(true);
				if (getOwner().getAllProfiles().isEmpty()) {
					menu.myprofiles.setVisible(false);
				} else {
					menu.myprofiles.setVisible(true);
				}
			} else {
				if (getOwner().getAllProfiles().isEmpty()) {// for normal user if user has one profile, dont show add profile.
					menu.addprofile.setVisible(true);
					menu.myprofiles.setVisible(false);
				} else {
					menu.addprofile.setVisible(false);
					menu.myprofiles.setVisible(true);
				}
			}
		}
	}

	public void profileApprovalChanged(Long id, boolean approved) {
		if (approved) {
			getOwner().getUnapporvedProfiles().remove(id);
			getOwner().getApprovedProfiles().add(id);
		} else {
			getOwner().getApprovedProfiles().remove(id);
			getOwner().getUnapporvedProfiles().add(id);
		}
	}

	public void profileUpdated(final Long id, final boolean isMap) {
		GreetingService.Util.getInstance().findMyProfilesById(Arrays.asList(id), new AsyncCallback<List<Profile>>() {
			@Override
			public void onSuccess(List<Profile> updatedProfile) {
				if (!updatedProfile.isEmpty()) {
					SearchResultViews resultView = (SearchResultViews) getView(View.SEARCH_RESULT);
					// TODO: check if to be cleaned or not
					resultView.profileUpdated(updatedProfile.get(0), isMap);
				}
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();
			}
		});

	}

	public void ratingThresholdChanged(Long currentRating) {
		String token = History.getToken();
		String[] vals = token.split(":");
		if (vals[0] != null && HistoryTokens.SEARCH_RESULT.equals(vals[0]) && vals[1] != null && !"".equals(vals[1])) {
			SearchBean s1 = UIUtil.decodeSearchBean(vals[1]);
			s1.setRatingThreshold(currentRating);
			String base64 = UIUtil.encodeSearchBean(s1);
			History.newItem(HistoryTokens.SEARCH_RESULT + ":" + base64, true);
		}
	}

	public void onValueChange(ValueChangeEvent<String> event) {
		// This method is called whenever the application's history changes.

		String token = event.getValue();
		if (HistoryTokens.DASHBOARD.equals(token)) {
			openDashboard();
		} else if (HistoryTokens.SEARCH.equals(token)) {
			openRegularSearch();
		} else if (HistoryTokens.SEARCH_ID.equals(token)) {
			openIDSearch();
		} else if (HistoryTokens.ADMIN.equals(token)) {
			openAdmin();
		} else if (HistoryTokens.SEARCH_UNAPPROVED.equals(token)) {
			openUnapprovedSearch();
		} else if (HistoryTokens.ADD_PROFILE.equals(token)) {
			openAddProfile();
		} else if (HistoryTokens.MY_PROFILES.equals(token)) {
			openMyProfiles();
		} else if (token.startsWith(HistoryTokens.MY_PROFILE_ID)) {
			openMyProfileId(token);
		} else if (token.startsWith(ProfileMetadata.IS)) {
			interestedProfiles(token);
		} else if (token.startsWith(ProfileMetadata.RA)) {
			interestedProfiles(token);
		} else if (token.startsWith(ProfileMetadata.RH)) {
			interestedProfiles(token);
		} else if (token.startsWith(ProfileMetadata.RR)) {
			interestedProfiles(token);
		} else if (token.startsWith(ProfileMetadata.IA)) {
			interestedProfiles(token);
		} else if (token.startsWith(ProfileMetadata.IH)) {
			interestedProfiles(token);
		} else if (token.startsWith(HistoryTokens.EDIT_PROFILE)) {
			openEditProfile(token);
		} else if (token.startsWith(HistoryTokens.SEARCH_RESULT)) {
			openSearchResults(token);
		} else if (token.startsWith(HistoryTokens.SEARCH_CRITERIA)) {
			openSpecificSearch();
		} else if (token.startsWith(HistoryTokens.ID_SEARCH_RESULT)) {
			openIdSearchResults(token);
		} else if (token.startsWith(HistoryTokens.SUCCESS)) {
			openSuccess();
		} else if (token.startsWith(HistoryTokens.CONTACT)) {
			openContact();
		}
		// else if (token.equals("help")) {
		// openHelp();
		// }

		// If you are adding an entry here, ass it to stage.java also.
	}

	private void openSuccess() {
		HasWidgets byahiContent = getCleanPage();
		Success view = (Success) getView(View.SUCCESS);
		byahiContent.add(view);
	}

	private void openContact() {
		HasWidgets byahiContent = getCleanPage();
		Contact view = (Contact) getView(View.CONTACT);
		byahiContent.add(view);
	}

	@SuppressWarnings("unused")
	@Deprecated
	private void openHelp() {
		HasWidgets byahiContent = getCleanPage();
		Frame frame = new Frame("http://www.byahi.com/public/help.html");
		frame.setStyleName("help-frame");
		frame.setTitle(Math.random() + "");
		byahiContent.add(frame);
	}

	private void openAdmin() {
		HasWidgets byahiContent = getCleanPage();
		AdminView view = (AdminView) getView(View.ADMIN);
		byahiContent.add(view);
	}

	/**
	 * When user clicks on a number on Summary page, it fetches those profiles, which fall into that category, For example, if user clicks interestes
	 * accepted links, then it fetches all profiles who have accepted user's interest.
	 * 
	 * @param token
	 */
	private void interestedProfiles(String token) {
		String[] vals = token.split(":");
		Long profileId = SharedUtil.getLong(vals[1]);
		setCurrentProfileId(profileId);// Async

		Set<Long> resultSet = interestDataComputed.get(currentProfileId).get(vals[0]);
		List<Long> result = new ArrayList<Long>(resultSet);
		showSearchResult(result, false);
	}

	private HasWidgets getCleanPage() {
		HasWidgets byahiContent = RootPanel.get("byahi-content");
		byahiContent.clear();

		// TODO: Menu is added all the time. optimise

		HasWidgets menuContent = RootPanel.get("menu-container");
		menuContent.clear();
		Menu m = (Menu) getView(View.MENU);
		menuContent.add(m);

		HasWidgets stagingContent = RootPanel.get("stage-container");
		stagingContent.clear();
		stagingContent.add(getView(View.STAGE));

		setLogoutString();

		return byahiContent;
	}

	private void setLogoutString() {
		HasWidgets logout = RootPanel.get("logout");
		logout.clear();
		if (currentProfileId == null) {
			logout.add(new HTML(initData.getCurrentUser() + " <a href=\"" + initData.getLogoutURL()
					+ "\"  id=\"signout-img\" alt=\"Sign out\"><img src=\"/images/exit.png\"></a>"));
		} else {
			logout.add(new HTML("Working on behalf of " + currentProfileName + " ( ID: " + currentProfileId + " )" + " <a href=\"" + initData.getLogoutURL()
					+ "\"  id=\"signout-img\" alt=\"Sign out\"><img src=\"/images/exit.png\"></a>"));
		}
	}

	@Deprecated
	public void showError(String type, String message) {
		HasWidgets errorContent = RootPanel.get("error-container");
		errorContent.clear();
		Error error = new Error();
		error.setError(type, message);
	}

	public void openRegularSearch() {
		HasWidgets byahiContent = getCleanPage();
		searchView = (ProfileSearchView) getView(View.SEARCH);
		searchView.clean();
		byahiContent.add(searchView);
	}

	public void openSpecificSearch() {
		String token = History.getToken();
		String[] vals = token.split(":");
		if (vals[0] != null && HistoryTokens.SEARCH_CRITERIA.equals(vals[0]) && vals[1] != null && !"".equals(vals[1])) {
			SearchBean s1 = UIUtil.decodeSearchBean(vals[1]);
			// String base64 = UIUtil.encodeSearchBean(s1);
			// History.newItem("search_result:" + base64, true);
			HasWidgets byahiContent = getCleanPage();
			searchView = (ProfileSearchView) getView(View.SEARCH);
			searchView.setValues(s1);
			byahiContent.add(searchView);
		}
	}

	public void openIDSearch() {
		HasWidgets byahiContent = getCleanPage();
		idSearchView = (ProfileIDSearchView) getView(View.IDSEARCH);
		byahiContent.add(idSearchView);
	}

	public void openSearchResults(String token) {
		String[] vals = token.split(":");
		if (vals[1] != null && !"".equals(vals[1])) {
			final SearchBean s1 = UIUtil.decodeSearchBean(vals[1]);

			GreetingService.Util.getInstance().findPublicProfileIds(s1, new AsyncCallback<List<Long>>() {

				@Override
				public void onSuccess(List<Long> result) {
					result.removeAll(getOwner().getAllProfiles());
					if (Config.filterRatingsInbrowser) {
						Map<Long, Set<Long>> computedProfileRatings = computeProfileRatings();
						Set<Long> allRatedProfiles = new HashSet<Long>();
						for (Set<Long> values : computedProfileRatings.values()) {
							allRatedProfiles.addAll(values);
						}

						Long selectedRating = s1.getRatingThreshold();
						if (Config.NO_RATING.equals(selectedRating)) {
							// All reated profiles are removed, only unrated profiles remain
							result.removeAll(allRatedProfiles);
						} else {
							// Only rated profiles are retained, now all unrated ones are gone.
							result.retainAll(allRatedProfiles);

							Set<Long> ratedAboveThreshold = new HashSet<Long>();
							for (Long i = selectedRating; i <= Config.MAX_RATING; i++) {// from threshold to max rating
								Set<Long> ratedIds = computedProfileRatings.get(i);
								ratedAboveThreshold.addAll(ratedIds);
							}
							result.retainAll(ratedAboveThreshold);
						}

					}
					showSearchResult(result, false);
				}

				@Override
				public void onFailure(Throwable caught) {
					UIUtil.showGenericFailureDialog();

				}
			});

		}

	}

	public void openIdSearchResults(String token) {
		String[] vals = token.split(":");
		if (vals[1] != null && !"".equals(vals[1])) {
			final Long id = new Long(vals[1]);
			GreetingService.Util.getInstance().verifyProfileIds(Arrays.asList(id), new AsyncCallback<List<Long>>() {

				@Override
				public void onSuccess(List<Long> result) {
					if (!result.isEmpty()) {
						result.removeAll(getOwner().getAllProfiles());
						showSearchResult(result, false);
					} else {
						showSearchResult(new ArrayList<Long>(), false);
					}
				}

				@Override
				public void onFailure(Throwable caught) {
					UIUtil.showGenericFailureDialog();
				}
			});

		}
	}

	public void showSearchResult(List<Long> result, boolean ownData) {
		HasWidgets byahiContent = getCleanPage();
		SearchResultViews resultView = (SearchResultViews) getView(View.SEARCH_RESULT);
		resultView.clean();
		byahiContent.add(resultView);
		resultView.showData(result, ownData);
	}

	/**
	 * @Deprecated public void showSearchResult(List<Profile> result) { HasWidgets byahiContent = getCleanPage(); SearchResultViews resultView =
	 *             (SearchResultViews) getView(View.SEARCH_RESULT); resultView.clean(); byahiContent.add(resultView); resultView.showData(result); }
	 */
	public void openUnapprovedSearch() {
		GreetingService.Util.getInstance().findAdminUnapprovedProfiles(new AsyncCallback<List<Long>>() {
			@Override
			public void onSuccess(List<Long> result) {
				AppController.get().showSearchResult(result, false);
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();

			}
		});
	}

	public void openEditProfile(Profile profile) {
		HasWidgets byahiContent = getCleanPage();
		byahiContent.add(getView(View.EDIT));
		profileUpdateView.setValues();
		profileUpdateView.attachMap();
		profileUpdateView.setProfile(profile);
	}

	private void openEditProfile(String token) {
		String[] vals = token.split(":");
		Long profileId = SharedUtil.getLong(vals[1]);
		GreetingService.Util.getInstance().findMyProfilesById(Arrays.asList(profileId), new AsyncCallback<List<Profile>>() {

			@Override
			public void onSuccess(List<Profile> profile) {
				openEditProfile(profile.get(0));
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();

			}
		});
	}

	public void openAddProfile() {
		Profile profile = new Profile();
		openEditProfile(profile);
	}

	public void openMyProfiles() {
		// Get all profile ids of user, including unapproved.
		GreetingService.Util.getInstance().findMyProfileIds(null, new AsyncCallback<List<Long>>() {

			@Override
			public void onSuccess(List<Long> result) {
				showSearchResult(result, true);
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();

			}
		});
	}

	/**
	 * Used for opening only one ID from summary page
	 * 
	 * @param id
	 */
	public void openMyProfileId(String token) {
		String[] vals = token.split(":");
		if (vals[1] != null && !"".equals(vals[1])) {
			final Long id = new Long(vals[1]);
			GreetingService.Util.getInstance().findMyProfileIds(null, new AsyncCallback<List<Long>>() {

				@Override
				public void onSuccess(List<Long> result) {
					if (result.contains(id)) {
						setCurrentProfileId(id);
						showSearchResult(Arrays.asList(id), true);
					} else {
						showSearchResult(new ArrayList<Long>(), true);
					}
				}

				@Override
				public void onFailure(Throwable caught) {
					UIUtil.showGenericFailureDialog();
				}
			});
		}
	}

	public void openDashboard() {
		HasWidgets byahiContent = getCleanPage();
		if (getOwner().getIsAgency() == null) {
			byahiContent.add((UserType) getView(View.USER_TYPE));
		} else {
			final FlexTable ft = (FlexTable) getView(View.DASHBOARD);
			Map<Long, Map<String, Set<Long>>> result = interestDataComputed;
			// TODO: add no profile message at row 0
			int row = 1;
			if (!result.isEmpty()) {
				boolean isAgency = AppController.get().getOwner().getIsAgency();
				if (!isAgency) {
					for (Entry<Long, Map<String, Set<Long>>> entry : result.entrySet()) {
						Summary summary = (Summary) getView(View.SUMMARY);
						summary.cleanForReuse();
						summary.setData(entry);
						ft.setWidget(row++, 0, summary);
						break; // only one profile.
					}
				} else {
					SummaryListView summaryListView = (SummaryListView) getView(View.SUMMARY_LIST);
					summaryListView.clean();
					summaryListView.setData(result, false);
					ft.setWidget(row++, 0, summaryListView);
				}
			} else {
				Error error = new Error();
				String text = "";
				if (AppController.get().getOwner().getAllProfiles().isEmpty()) {
					text = "You do not have any profile yet. Please create at least one profile.\n From the menu above, please select Profile>Add profile";
				} else if (AppController.get().getOwner().getApprovedProfiles().isEmpty()) {
					text = "You do not have any approved profile yet. Please wait for your profiles to be approved.";
				}

				error.setError(Error.WARNING, text);
				ft.setWidget(row++, 0, error);
				// Add empty summary to give user an idea.
				ft.setWidget(row, 0, new Summary());
			}

			byahiContent.add(ft);
		}
	}

	public ProfileOwner getOwner() {
		return initData.getOwner();
	}

	public Boolean getCurrentUserAdmin() {
		return initData.getCurrentUserAdmin();
	}

	public Map<Long, Long> getRatings() {
		return selectedProfileRatings;
	}

	public void ratingChanged(final Long profileId, final Long rating) {
		// Change it on UI first, keep on saving in background
		getRatings().put(profileId, rating);

		// Notify search result views that rating is changed, to hide profile if needed
		((SearchResultViews) getView(View.SEARCH_RESULT)).ratingChanged(profileId, rating);
		// Now keep on saving in database.
		GreetingService.Util.getInstance().rateProfile(profileId, rating, new AsyncCallback<Boolean>() {
			@Override
			public void onSuccess(Boolean result) {
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();
			}
		});

	}

	public static AppController get() {
		return controller;
	}

	public List<String> getSbList() {
		return initData.getSbList();
	}

	public Long getCurrentProfileId() {
		return currentProfileId;
	}

	// Friendly access
	InitDataVO getInitData() {
		return initData;
	}
}
